﻿using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace lib
{
    public class SecureConnectionServer
    {
        private SslStream sslStream = null;
        private X509Certificate serverCertificate = null;
        private BinaryFormatter formatter = new BinaryFormatter();

        public SecureConnectionServer(TcpClient tcp, X509Certificate cert)
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
            serverCertificate = cert;
            init(tcp);
        }

        public void ClosesslStream()
        {
            sslStream.Close();
        }

        public void setServerCertificate(X509Certificate cert)
        {
            serverCertificate = cert;
        }

        public void init(TcpClient tcp)
        {
            if (serverCertificate == null) return;
            sslStream = new SslStream(tcp.GetStream());
            Authenticate();
        }

        private void Authenticate()
        {
            try
            {
                sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Default, false);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
                }
                Console.WriteLine("Authentication failed");
                sslStream = null;
            }
        }

        public void SendMessage(string message)
        {
            Byte[] sendBytes = Encoding.UTF8.GetBytes(message);
            sslStream.Write(sendBytes, 0, sendBytes.Length);
        }

        public string ReadMessage()
        {
            byte[] bytes = new byte[2048];

            // Read can return anything from 0 to numBytesToRead.  
            // This method blocks until at least one byte is read.
            sslStream.Read(bytes, 0, 2048);

            // Returns the data received from the host to the console. 
            string s = Encoding.UTF8.GetString(bytes);
            return s.Remove(s.IndexOf("\0"));

        }

        public void SendObject(Object o)
        {
            try
            {
                formatter.Serialize(sslStream, o);
            }
            catch { }
        }

        public String ReadObjectAsString()
        {
                return formatter.Deserialize(sslStream) as String;
        }

        public Object ReadObject()
        {
            return formatter.Deserialize(sslStream);
        }

        public static void DisplaySecurityLevel(SslStream stream)
        {
            Console.WriteLine("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
            Console.WriteLine("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
            Console.WriteLine("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
            Console.WriteLine("Protocol: {0}", stream.SslProtocol);
        }
        public static void DisplaySecurityServices(SslStream stream)
        {
            Console.WriteLine("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
            Console.WriteLine("IsSigned: {0}", stream.IsSigned);
            Console.WriteLine("Is Encrypted: {0}", stream.IsEncrypted);
        }
        public static void DisplayStreamProperties(SslStream stream)
        {
            Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
            Console.WriteLine("Can timeout: {0}", stream.CanTimeout);
        }
        public static void DisplayCertificateInformation(SslStream stream)
        {
            Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);

            X509Certificate localCertificate = stream.LocalCertificate;
            if (stream.LocalCertificate != null)
            {
                Console.WriteLine("Local cert was issued to {0} and is valid from {1} until {2}.",
                    localCertificate.Subject,
                    localCertificate.GetEffectiveDateString(),
                    localCertificate.GetExpirationDateString());
            }
            else
            {
                Console.WriteLine("Local certificate is null.");
            }
            // Display the properties of the client's certificate.
            X509Certificate remoteCertificate = stream.RemoteCertificate;
            if (stream.RemoteCertificate != null)
            {
                Console.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.",
                    remoteCertificate.Subject,
                    remoteCertificate.GetEffectiveDateString(),
                    remoteCertificate.GetExpirationDateString());
            }
            else
            {
                Console.WriteLine("Remote certificate is null.");
            }
        }
    }
}
